import wx
from twisted.internet import wxreactor
wxreactor.install()

import cPickle
from message import Message
import cardplayer
import time
from common.cred import Credentials
import common.defines as defines

# import twisted reactor *only after* installing wxreactor
from twisted.internet import reactor, protocol
from twisted.protocols import basic

connected = False
authed = False

class Login(wx.Dialog):
    '''
    Pop up panel for collecting user credentials and logging into the netjack server.
    '''

    def __init__(self, *args, **kw):
        super(Login, self).__init__(*args, title=kw['title'])

        self.InitUI()
        self.SetSize((400, 250))
        self.SetTitle("Login to NetJack")
        self.protocol = kw['protocol']

    def InitUI(self):
        pnl = wx.Panel(self)
        vbox = wx.BoxSizer(wx.VERTICAL)

        sb = wx.StaticBox(pnl, label="Login")
        sbs = wx.StaticBoxSizer(sb, orient=wx.VERTICAL)
        self.usernameTxt = wx.TextCtrl(pnl, style=wx.TE_PROCESS_ENTER)
        self.passwordTxt = wx.TextCtrl(pnl, style=wx.TE_PASSWORD | wx.TE_PROCESS_ENTER)
        self.serverTxt = wx.TextCtrl(pnl, style=wx.TE_PROCESS_ENTER)
        self.lblUsername = wx.StaticText(pnl, label="Username:")
        self.lblPassword = wx.StaticText(pnl, label="Password:")
        self.lblServer = wx.StaticText(pnl, label="Server:")
        sbs.Add(self.lblUsername, 1, wx.EXPAND)
        sbs.Add(self.usernameTxt, 1, wx.EXPAND)
        sbs.Add(self.lblPassword, 1, wx.EXPAND)
        sbs.Add(self.passwordTxt, 1, wx.EXPAND)
        sbs.Add(self.lblServer, 1, wx.EXPAND)
        sbs.Add(self.serverTxt, 1, wx.EXPAND)

        pnl.SetSizer(sbs)

        hbox2 = wx.BoxSizer(wx.HORIZONTAL)
        loginButton = wx.Button(self, label='Login')
        closeButton = wx.Button(self, label='Cancel')
        hbox2.Add(loginButton)
        hbox2.Add(closeButton, flag=wx.LEFT, border=5)

        vbox.Add(pnl, proportion=1,
            flag=wx.ALL | wx.EXPAND, border=5)
        vbox.Add(hbox2,
            flag=wx.ALIGN_CENTER | wx.TOP | wx.BOTTOM, border=10)

        self.SetSizer(vbox)

        loginButton.Bind(wx.EVT_BUTTON, self.OnLogin)
        closeButton.Bind(wx.EVT_BUTTON, self.OnClose)
        self.usernameTxt.Bind(wx.EVT_TEXT_ENTER, self.OnLogin)
        self.passwordTxt.Bind(wx.EVT_TEXT_ENTER, self.OnLogin)
        self.serverTxt.Bind(wx.EVT_TEXT_ENTER, self.OnLogin)

        self.usernameTxt.SetFocus()

    def OnClose(self, e):
        self.Destroy()

    def OnLogin(self, e):
        '''
        Create an AUTH_INIT message with current credentials and send to server.
        '''
        global username, password, server
        username = str(self.usernameTxt.GetValue())
        password = str(self.passwordTxt.GetValue())
        server = str(self.serverTxt.GetValue())

        Connect(server)

        self.OnClose(e)

class NoUnsplitter(wx.SplitterWindow):
    def __init__(self, parent, id):
        wx.SplitterWindow.__init__(self, parent, id)
    def Unsplit(self, toRemove):
        self.SetSashPosition(10, True)

class TablePanel(wx.Panel):
    def __init__(self, parent):
        wx.Panel.__init__(self, parent)
        self.deck_sprite = wx.Bitmap('resources/52_100.png')
        self.felt = wx.Bitmap('resources/felt_best.png')
        self.chip = wx.Bitmap('resources/currentPlayer.png')
        self.Bind(wx.EVT_PAINT, self.OnPaint)
        self.players = None

    def OnPaint(self, event):
        # Render Table Panel
        dc = wx.PaintDC(self)
        dc.DrawBitmap(self.felt, 0, 0)

#        self.players[0].render_hand(dc, self.deck_sprite, 0)

        try:
            # Render Player
            if self.players:
                for player in range(0, 5):
                    if self.players[player]:
                        self.players[player].render_hand(dc, self.deck_sprite, self.chip, player)
                self.players[5].render_hand(dc, self.deck_sprite, self.chip, 5, True)
        except Exception, ex:
            print ex

    def UpdatePlayers(self, players):
        self.players = players

class ChatFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, parent=None, title="Netjack", size=(850, 800))

        self.nextAction = 0

        self.protocol = None # twisted Protocol
        self.name = "Unnamed"
        self.players = None

        self.sb = self.CreateStatusBar()

        # setting up the menus
        filemenu = wx.Menu()
        ID_LOGIN = wx.NewId()
        self.menuEntrylogin = filemenu.Append(ID_LOGIN, "&Login", "Login to NetJack")
        filemenu.AppendSeparator()
        filemenu.Append(wx.ID_EXIT, "E&xit", " Terminate the program")

        # Create the menubar
        menuBar = wx.MenuBar()
        menuBar.Append(filemenu, "&NetJack")
        self.SetMenuBar(menuBar)

        self.superSizer = wx.BoxSizer(wx.VERTICAL)
        self.splitter = NoUnsplitter(self, -1)
        self.superSizer.Add(self.splitter, 1, wx.EXPAND, 10)
        self.splitter.SetSashSize(10)
        self.splitter.SetMinimumPaneSize(25)

#        self.panelTable = wx.Panel(self.splitter)
        self.panelTable = TablePanel(self.splitter)
        self.panelControl = wx.Panel(self.splitter)

        self.splitter.SplitHorizontally(self.panelTable, self.panelControl, 400)

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.innerSizer = wx.BoxSizer(wx.HORIZONTAL)

        self.text = wx.TextCtrl(self.panelControl, style=wx.TE_MULTILINE | wx.TE_READONLY)
        self.ctrl = wx.TextCtrl(self.panelControl, style=wx.TE_PROCESS_ENTER | wx.TE_MULTILINE)
#        self.txtBet = wx.StaticText(self.panelControl, label="Bet amount:")
        self.spinBet = wx.SpinCtrl(self.panelControl, -1)
        self.spinBet.SetRange(1, 1000000)
        self.spinBet.SetValue(5)

        self.bHit = wx.Button(self.panelControl, -1, "Hit")
        self.bStand = wx.Button(self.panelControl, -1, "Stand")
        self.bBet = wx.Button(self.panelControl, -1, "Set Bet Amount")
        self.textBank = wx.TextCtrl(self.panelControl, style=wx.TE_READONLY)
        self.textBank.Disable()

        self.bHit.Disable()
        self.bStand.Disable()

        self.sizer.Add(self.text, 5, wx.EXPAND)
        self.sizer.Add(self.ctrl, 2, wx.EXPAND)


        self.innerSizer.Add(self.bHit, 0, border=10)
        self.innerSizer.Add(self.bStand, 0, border=10)
#        self.innerSizer.Add(self.txtBet, 0, border=10)
        self.innerSizer.AddStretchSpacer()
        self.innerSizer.Add(self.spinBet, 0, border=10)
        self.innerSizer.Add(self.bBet, 0, border=10)
        self.innerSizer.Add(self.textBank, 1, 10, wx.EXPAND)
        self.sizer.Add(self.innerSizer, 0, wx.EXPAND)

#        self.SetSizer(self.sizer)

        self.ctrl.Bind(wx.EVT_TEXT_ENTER, self.send)
        self.bHit.Bind(wx.EVT_BUTTON, self.hit)
        self.bStand.Bind(wx.EVT_BUTTON, self.stand)
        self.bBet.Bind(wx.EVT_BUTTON, self.bet)
        self.Bind(wx.EVT_MENU, self.OnLoginMenu, id=ID_LOGIN)
        self.Bind(wx.EVT_MENU, self.OnClose, id=wx.ID_EXIT)

        self.panelControl.SetSizer(self.sizer)
        self.ctrl.SetFocus()

#        self.Layout()
#        self.SetAutoLayout(True)
#        self.sizer.Fit(self.superSizer)

        self.SetSizer(self.superSizer)

        self.Layout()
        self.Centre()

        self.OnTimer(None)
        self.timer = wx.Timer(self, -1)
        # update clock digits every second (1000ms)
        self.timer.Start(1000)
        self.Bind(wx.EVT_TIMER, self.OnTimer)

    def OnClose(self, event):
        dlg = wx.MessageDialog(self,
            "Do you really want to close this application?",
            "Confirm Exit", wx.OK | wx.CANCEL | wx.ICON_QUESTION)
        result = dlg.ShowModal()
        dlg.Destroy()
        if result == wx.ID_OK:
            self.Destroy()

    def OnTimer(self, event):
        # get current time from computer
#        current = time.localtime(time.time())
        # time string can have characters 0..9, -, period, or space
#        ts = time.strftime("%H %M %S", current)
        self.sb.SetStatusText("Time until next action: {}".format(self.nextAction))
        if self.nextAction > 0:
            self.nextAction -= 1

    def send(self, evt):
        if authed:
            self.protocol.sendLine(cPickle.dumps(Message("chat", str(self.ctrl.GetValue()))) + "\t")
            self.ctrl.SetValue("")
#        else:
#            self.protocol.sendLine(cPickle.dumps(message.Message("auth", str(self.ctrl.GetValue()))) + "\t")
#            self.ctrl.SetValue("")

    def hit(self, evt):
        self.nextAction += 2
        self.protocol.sendLine(cPickle.dumps(Message('hit')))

    def stand(self, evt):
        self.nextAction = 0
        self.protocol.sendLine(cPickle.dumps(Message('stand')))

    def bet(self, evt):
        self.protocol.sendLine(cPickle.dumps(Message('bet', self.spinBet.GetValue())))

    def OnFail(self, event=None):
        wx.MessageDialog(self, "Connection to server lost.", "Error", wx.OK | wx.ICON_HAND).ShowModal().Destroy()
        self.menuEntrylogin.Enable(True)
#        dlg = wx.MessageDialog(self, "Unable to connect to server.", "Connection Error", wx.OK)
#        dlg.ShowModal()
#        dlg.Destroy()
#        self.Destroy()

    def DisplayPlayers(self):
        self.panelTable.UpdatePlayers(self.players)
        self.panelTable.Refresh()
#        val = self.text.GetValue()
#        for player in self.players:
#            if player:
#                val += player.name + str(player._get_current_hand()) + '\n'
#        val += '---------------------\n'
#        self.text.SetValue(val)
#        self.text.SetInsertionPointEnd()


    def Enable(self):
        self.bHit.Enable()
        self.bStand.Enable()

    def Disable(self):
        self.bHit.Disable()
        self.bStand.Disable()

    def OnLoginMenu(self, e=None):
        self.login = Login(self, protocol=self.protocol, title="Login to NetJack")
        self.login.ShowModal()


class DataForwardingProtocol(basic.LineReceiver):
    def __init__(self):
        self.output = None
        self.lineBuffer = None

    def dataReceived(self, data):
#        print "-----Begin Data Received-----"
        gui = self.factory.gui
#        print "---Begin Raw Receive---\n" + repr(data) + "\n---End Raw Recieve"
        lines = data.rstrip("\t\r\n").split("\t")
        # lines.pop()
        if self.lineBuffer:
            lines[0] = self.lineBuffer + lines[0]
            self.lineBuffer = None
        for stripMe, num in zip(lines, range(len(lines))):
            item = stripMe.strip("\r\n")
#            print "***Begin Pickle #" + str(num + 1) + "***"
#            print repr(item)
#            print "***End Pickle #" + str(num + 1) + "***"
            try:
                undata = cPickle.loads(item)
                if isinstance(undata, Message):
#                    print type(undata)
                    if undata.type == "chat":
                        gui.protocol = self
                        if gui:
                            val = gui.text.GetValue()
                            gui.text.SetValue(val + undata.payload + "\n")
                            gui.text.SetInsertionPointEnd()
                    elif undata.type == "userStates":
#                        self.factory.gui.nextAction = undata.timer
                        self.factory.gui.players = undata.payload
                        self.factory.gui.DisplayPlayers()
#                        print self.factory.gui.players
                        for player in self.factory.gui.players:
                            if player:
#                                print player.name + "'s turn:" + str(player.myTurn)
                                if player.name == gui.name:
                                    gui.textBank.SetValue('Bank: $' + str(player.bank))
                                    if player.myTurn == True:
                                        gui.Enable()
                                    else:
                                        gui.Disable()
                    elif undata.type == "timer":
                        self.factory.gui.nextAction = undata.payload
                    elif undata.type == "auth":
                        temp = undata.payload.split(':')
                        if temp[0] == "okay":
                            gui.menuEntrylogin.Enable(False)
                            gui.bet()
                            global authed
                            authed = True
                            gui.name = temp[1]
                        else:
                            if gui:
                                val = gui.text.GetValue()
                                gui.text.SetValue(val + 'Error: ' + temp[1] + "\n")
                                gui.text.SetInsertionPointEnd()
                                self.factory.loseConnection()

#                                gui.OnLoginMenu()
                else:
                    print "ERROR: Data was not a Message object."
                    print "-----End Data Received-----"
 #            except (cPickle.UnpicklingError, EOFError) as e:
            except :
                self.lineBuffer = stripMe
#                print "Error unpickling network object."
#                print type(e)

    def connectionMade(self):
        self.output = self.factory.gui.text # redirect Twisted's output
        global password
        cred = Credentials(username, password)
        msg = Message('auth', cred)
        self.sendLine(cPickle.dumps(msg) + "\t")
        del cred
        del msg
        del password

class ChatFactory(protocol.ClientFactory):
    def __init__(self, gui):
        self.gui = gui
        self.protocol = DataForwardingProtocol

    def clientConnectionLost(self, transport, reason):
        self.gui.OnFail()

    def clientConnectionFailed(self, transport, reason):
        pass
# end of class NetJack

frameGame = None

def Connect(host):
    temp = host.split(':')
    if len(temp) == 2:
        port = int(temp[1])
    else:
        port = 5001
    if temp[0] == '':
        return
    elif temp[0] != '127.0.0.1':
        hostname = temp[0]
    else:
        hostname = 'localhost'
    reactor.connectTCP(hostname, port, ChatFactory(frameGame))

class mainGUI(wx.App):
    def OnInit(self):
        wx.InitAllImageHandlers()
        global frameGame
        frameGame = ChatFrame()
        frameGame.Show()

        return 1

if __name__ == '__main__':
    reactor.registerWxApp(mainGUI(0))
    reactor.run()
